﻿/*******************************************************************************

    Units of Measure for C# applications

    Copyright (C) 2013 Marek Aniola

    This program is provided to you under the terms of the license
    as published at http://unitsofmeasure.codeplex.com/license


********************************************************************************/
using System;

namespace Man.UnitsOfMeasure
{
	public class Level<T> : IComparable<Level<T>>, IEquatable<Level<T>> where T : struct
	{
		#region Properties

		private T m_value;
		public T Value { get { return m_value; } }

		private Measure<T> m_unit;
		public Measure<T> Unit { get { return m_unit; } }

		#endregion

		#region Constructor(s)
		public Level(T value, Measure<T> unit)
		{
			m_value = value;
			m_unit = unit;
		}
		#endregion Constructor(s)

		#region Conversions

		public Level<T> this[Measure<T> targetUnit]
		{
			get { return new Level<T>(this / targetUnit, targetUnit); }
		}

		public static T operator / (Level<T> level, Measure<T> targetUnit)
		{
			return TypeHost.Calibrate(level, targetUnit);
		}

		#endregion Conversions

		#region IEquatable, IComparable

		public override int GetHashCode()
		{
			unchecked
			{
				int hash = 17;
				hash = hash * 23 + m_value.GetHashCode();
				hash = hash * 23 + m_unit.GetHashCode();
				return hash;
			}
		}

		public override bool Equals(object obj)
		{
			return (obj != null) && (obj is Level<T>) && Equals((Level<T>)obj);
		}

		public bool Equals(Level<T> other)
		{
			return !object.Equals(other, null) && (TypeHost.Compare(this, other) == 0);
		}

		public static bool operator ==(Level<T> x, Level<T> y)
		{
			return object.ReferenceEquals(x, y) || (!object.Equals(x, null) && x.Equals(y));
		}

		public static bool operator !=(Level<T> x, Level<T> y)
		{
			return !(x == y);
		}

		public static bool operator <(Level<T> x, Level<T> y)
		{
			return TypeHost.Compare(x, y) < 0;
		}

		public static bool operator >(Level<T> x, Level<T> y)
		{
			return TypeHost.Compare(x, y) > 0;
		}

		public static bool operator <=(Level<T> x, Level<T> y)
		{
			return TypeHost.Compare(x, y) <= 0;
		}

		public static bool operator >=(Level<T> x, Level<T> y)
		{
			return TypeHost.Compare(x, y) >= 0;
		}

		public int CompareTo(Level<T> other)
		{
			return TypeHost.Compare(this, other);
		}

		#endregion Comparison

		#region Operators

		public static Level<T> operator +(Level<T> x, Quantity<T> q)
		{
			return new Level<T>(TypeHost.Add(x.Value, q / x.Unit), x.Unit);
		}

		public static Level<T> operator +(Quantity<T> q, Level<T> y)
		{
			return new Level<T>(TypeHost.Add(y / q.Unit, q.Value), q.Unit);
		}

		public static Level<T> operator ++(Level<T> x)
		{
			return new Level<T>(TypeHost.Add(x.Value, TypeHost.Unity), x.Unit);
		}

		public static Level<T> operator -(Level<T> x)
		{
			return new Level<T>(TypeHost.Negative(x.Value), x.Unit);
		}

		public static Quantity<T> operator -(Level<T> x, Level<T> y)
		{
			return new Quantity<T>(TypeHost.Subtract(x.Value, y / x.Unit), x.Unit);
		}

		public static Level<T> operator -(Level<T> x, Quantity<T> y )
		{
			return new Level<T>(TypeHost.Subtract(x.Value, y / x.Unit), x.Unit);
		}

		public static Level<T> operator --(Level<T> x)
		{
			return new Level<T>(TypeHost.Subtract(x.Value, TypeHost.Unity), x.Unit);
		}

		public static Level<T> operator *(T factor, Level<T> x)
		{
			return new Level<T>(TypeHost.Multiply(factor, x.Value), x.Unit);
		}

		public static Level<T> operator *(Level<T> x, T factor)
		{
			return new Level<T>(TypeHost.Multiply(factor, x.Value), x.Unit);
		}

		public static Level<T> operator /(Level<T> x, T denominator)
		{
			return new Level<T>(TypeHost.Divide(x.Value, denominator), x.Unit);
		}

		#endregion Operators

		#region Dimensional analysis

		public static bool Convertible(Level<T> x, Level<T> y)
		{
			return (x.Unit.Sense == y.Unit.Sense);
		}

		#endregion Dimensional analysis

		#region Formatting

		public override string ToString()
		{
			return ToString(null, FormatPattern);
		}

		public string ToString(string format)
		{
			return ToString(null, format);
		}

		public string ToString(IFormatProvider fp)
		{
			return ToString(fp, FormatPattern);
		}

		public string ToString(IFormatProvider fp, string format)
		{
			var unit = m_unit as UnitOfMeasure<T>;
			return (unit == null) ?
				String.Format(fp, format, m_value, String.Empty, m_unit.ToString(fp)) :
				String.Format(fp, format, m_value, unit.Separator, unit.Symbol(0));
		}

		#endregion Formatting

		#region Statics

		private static string s_format = "{0}{1}{2}";	// {0:value}{1:separator}{2:unit|measure}
		public static string FormatPattern { get { return s_format; } set { s_format = value; } }

		private static ITypeHost<T> TypeHost = Measure<T>.InitializeTypeHost();

		#endregion Statics
	}
}
